#pragma once

#include <utility>
#include <memory>
#include <type_traits>
#include <functional>

#define ZENO_F(...) ([] (auto &&...__args) -> decltype(auto) { return __VA_ARGS__(std::forward<decltype(__args)>(__args)...); })
#define ZENO_RMCVREF(...) std::remove_cvref_t<decltype(__VA_ARGS__)>
#define ZENO_DECAY(...) std::decay_t<decltype(__VA_ARGS__)>
#define ZENO_FWD(x) std::forward<decltype(x)>(x)
#define ZENO_CRTP(Derived, Base, ...) Derived : Base<Derived __VA_ARGS__>

namespace zenox {

template <class To, class T>
inline constexpr To implicit_cast(T &&t) {
    return std::forward<T>(t);
}

template <class To, class T>
inline constexpr To braced_cast(T &&t) {
    return To{std::forward<T>(t)};
}

template <class To, class T>
inline constexpr To cstyle_cast(T &&t) {
    return To(std::forward<T>(t));
}

template <class ...Fs>
struct overloaded : Fs... {
    using Fs::operator()...;
};

template <class ...Fs>
overloaded(Fs...) -> overloaded<Fs...>;

template <class T>
struct function_traits : function_traits<decltype(&T::operator())> {
};

template <class R, class... Args>
struct function_traits<R(Args...)> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
};

template <class R, class... Args>
struct function_traits<R(Args...) noexcept> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
    using is_noexcept = std::true_type;
};

template <class R, class... Args>
struct function_traits<R (*)(Args...)> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
};

template <class R, class... Args>
struct function_traits<R (*)(Args...) noexcept> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
    using is_noexcept = std::true_type;
};

template <class R, class... Args>
struct function_traits<std::function<R(Args...)>> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
    using is_std_function = std::true_type;
};

template <class T, class R, class... Args>
struct function_traits<R (T::*)(Args...)> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
    using is_member = std::true_type;
};

template <class T, class R, class... Args>
struct function_traits<R (T::*)(Args...) const> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
    using is_member = std::true_type;
    using is_const_member = std::true_type;
};

template <class T, class R, class... Args>
struct function_traits<R (T::*)(Args...) noexcept> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
    using is_member = std::true_type;
    using is_noexcept = std::true_type;
};

template <class T, class R, class... Args>
struct function_traits<R (T::*)(Args...) const noexcept> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
    using is_member = std::true_type;
    using is_const_member = std::true_type;
    using is_noexcept = std::true_type;
};

// usage example:
// static_for<0, n>([&] (auto index) {
//     std::array<int, index.value> arr;
//     return false;   // true to break the loop
// });
template <auto First, auto Last, class F>
inline constexpr bool static_for(F const &f) {
    if constexpr (First != Last) {
        using I = std::integral_constant<decltype(First), First>;
        if constexpr (std::is_void_v<std::invoke_result_t<F, I>>) {
            std::invoke(f, I{});
        } else {
            if (std::invoke(f, I{})) {
                return true;
            } else {
                return static_for<First + 1, Last>(f);
            }
        }
    }
    return false;
}

template <class Fn>
class move_only_function;

template <class Ret, class ...Args>
class move_only_function<Ret(Args...)> {
    struct Base {
        virtual void operator()(Args ...) = 0;
    };

    template <class Fn>
    struct Impl : Base {
        Fn m_fn;

        explicit Impl(Fn fn) noexcept : m_fn(std::forward<Fn>(fn)) {}

        void operator()(Args ...args) override {
            std::invoke(m_fn, std::forward<Args>(args)...);
        }
    };

    std::unique_ptr<Base> m_p;

public:
    template <class Fn, std::enable_if_t<
        !std::is_same_v<std::remove_cvref_t<Fn>, move_only_function> &&
        std::is_invocable_r_v<Ret, Fn &, Args...>, int> = 0>
    move_only_function(Fn &&fn)
    : m_p(std::make_unique<Impl<std::remove_cvref_t<Fn>>>(std::forward<Fn>(fn)))
    {}

    explicit operator bool() const noexcept {
        return !!m_p;
    }

    void operator()(Args ...args) {
        m_p->operator()(std::forward<Args>(args)...);
    }
};

template <class Ret, class ...Args>
class move_only_function<Ret(Args...) const> {
    struct Base {
        virtual void operator()(Args ...) const = 0;
    };

    template <class Fn>
    struct Impl : Base {
        Fn m_fn;

        explicit Impl(Fn fn) noexcept : m_fn(std::forward<Fn>(fn)) {}

        void operator()(Args ...args) const override {
            std::invoke(m_fn, std::forward<Args>(args)...);
        }
    };

    std::unique_ptr<Base> m_p;

public:
    template <class Fn, std::enable_if_t<
        !std::is_same_v<std::remove_cvref_t<Fn>, move_only_function> &&
        std::is_invocable_r_v<Ret, Fn const &, Args...>, int> = 0>
    move_only_function(Fn &&fn)
    : m_p(std::make_unique<Impl<std::remove_cvref_t<Fn>>>(std::forward<Fn>(fn)))
    {}

    explicit operator bool() const noexcept {
        return !!m_p;
    }

    void operator()(Args ...args) {
        m_p->operator()(std::forward<Args>(args)...);
    }
};

template <class Ret, class ...Args>
class move_only_function<Ret(Args...) noexcept> {
    struct Base {
        virtual void operator()(Args ...) noexcept = 0;
    };

    template <class Fn>
    struct Impl : Base {
        Fn m_fn;

        explicit Impl(Fn fn) noexcept : m_fn(std::forward<Fn>(fn)) {}

        void operator()(Args ...args) noexcept override {
            std::invoke(m_fn, std::forward<Args>(args)...);
        }
    };

    std::unique_ptr<Base> m_p;

public:
    template <class Fn, std::enable_if_t<
        !std::is_same_v<std::remove_cvref_t<Fn>, move_only_function> &&
        std::is_nothrow_invocable_r_v<Ret, Fn &, Args...>, int> = 0>
    move_only_function(Fn &&fn)
    : m_p(std::make_unique<Impl<std::remove_cvref_t<Fn>>>(std::forward<Fn>(fn)))
    {}

    explicit operator bool() const noexcept {
        return !!m_p;
    }

    void operator()(Args ...args) {
        m_p->operator()(std::forward<Args>(args)...);
    }
};

template <class Ret, class ...Args>
class move_only_function<Ret(Args...) const noexcept> {
    struct Base {
        virtual void operator()(Args ...) const noexcept = 0;
    };

    template <class Fn>
    struct Impl : Base {
        Fn m_fn;

        explicit Impl(Fn fn) noexcept : m_fn(std::forward<Fn>(fn)) {}

        void operator()(Args ...args) const noexcept override {
            std::invoke(m_fn, std::forward<Args>(args)...);
        }
    };

    std::unique_ptr<Base> m_p;

public:
    template <class Fn, std::enable_if_t<
        !std::is_same_v<std::remove_cvref_t<Fn>, move_only_function> &&
        std::is_nothrow_invocable_r_v<Ret, Fn const &, Args...>, int> = 0>
    move_only_function(Fn &&fn)
    : m_p(std::make_unique<Impl<std::remove_cvref_t<Fn>>>(std::forward<Fn>(fn)))
    {}

    explicit operator bool() const noexcept {
        return !!m_p;
    }

    void operator()(Args ...args) const noexcept {
        m_p->operator()(std::forward<Args>(args)...);
    }
};

}
